package com.example.hospital.patient.wx.api.Service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.example.hospital.patient.wx.api.Service.FaceAuthService;
import com.example.hospital.patient.wx.api.Service.RegistrationService;
import com.example.hospital.patient.wx.api.db.dao.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Baiken
 * @version 1.0
 * @description: 挂号界面数据
 * @date 2024/9/15 下午2:14
 */

@Service
public class RegistrationServiceImpl implements RegistrationService {


    @Resource
    private UserDao userDao;

    @Resource
    private RedisTemplate redisTemplate;


    @Resource
    private DoctorWorkPlanDao doctorWorkPlanDao;

    @Resource
    private MedicalRegistrationDao medicalRegistrationDao;

    @Resource
    private UserInfoCardDao userInfoCardDao;

    @Resource
    private FaceAuthService faceAuthService;
    @Resource
    private DoctorWorkPlanScheduleDao doctorWorkPlanScheduleDao;

    @Override
    public ArrayList<HashMap> searchCanRegisterInDateRange(Map param) {
        ArrayList<String> list = doctorWorkPlanDao.searchCanRegisterInDateRange(param);
        DateTime startDate = DateUtil.parse(MapUtil.getStr(param,"startDate"));
        DateTime endDate = DateUtil.parse(MapUtil.getStr(param,"endDate"));
        DateRange range = DateUtil.range(startDate,endDate, DateField.DAY_OF_MONTH);
        ArrayList result = new ArrayList();
        while(range.hasNext()){
            String date= range.next().toDateStr();
            if(list.contains(date)){
                result.add(new HashMap(){{
                    put("date",date);
                    put("status","出诊");
                }});
            }else{
                result.add(new HashMap(){{
                    put("date",date);
                    put("status","无号");
                }});
            }
        }
        return result;
    }

    @Override
    public String checkRegisterCondition(Map param) {
        //检查是否超过三次
        param.put("today",DateUtil.today());
        long count = medicalRegistrationDao.searchRegistrationCountInToday(param);
        if(count == 3){
            return "已经达到当天挂号上限";
        }
        //检查是否已经挂过该门诊
        Integer id = medicalRegistrationDao.hasRegisterRecordInDay(param);
        if(id != null){
            return "已经挂过该诊室的号";
        }
        //检查是否人脸
        Integer i = MapUtil.getInt(param,"userId");
        Boolean bool = userInfoCardDao.searchExistFaceModel(i);
        if(bool == null || !bool){
            return "不存在面部模型";
        }
        //今日是否面部
        bool = faceAuthService.hasFaceAuthInDay(param);
        if(!bool){
            return "当日没有人脸验证记录";
        }
        return "满足挂号条件";
    }

    @Override
    public ArrayList<HashMap> searchDoctorWorkPlanSchedule(Map param) {
        return doctorWorkPlanScheduleDao.searchDoctorWorkPlanSchedule(param);
    }

    @Override
    @Transactional
    public HashMap registerMedicalAppointment(Map param) {
        int workPlanId = MapUtil.getInt(param,"workPlanId");
        int scheduleId = MapUtil.getInt(param,"scheduleId");

        //检查redis中是否有日程缓存,没有则不进行挂号
        String key = "docker_schedule_" + scheduleId;
        if(!redisTemplate.hasKey(key)){
            return null;
        }
        //redis事务代码必须写道execute()回调函数中
        Object execute = redisTemplate.execute(new SessionCallback() {

            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //关注缓存数据(拿到乐观锁的version)
                operations.watch(key);
                //拿到缓存数据
                Map entry = operations.opsForHash().entries(key);
                int maximum = Integer.parseInt(entry.get("maximum").toString());
                int num = Integer.parseInt(entry.get("num").toString());
                //如果已经挂号人数小于最大人数就可以挂号
                if(num < maximum){
                    //开启事务
                    operations.multi();
                    //自增1
                    operations.opsForHash().increment(key,"num",1);
                    //提交事务
                    return operations.exec();
                }else{
                    operations.unwatch();
                    return null;
                }
            }
        });
        //如果redis事务提交失败,就结束service方法
        if(execute == null){
            return null;
        }
        try{
            //TODO 创建订单
            //TODO 保存挂号记录
            //TODO 更新数据库中的该时段挂号人数和该医生当日实际挂号人数
            //TODO 在redis中缓存付款记录,设置过期时间
        }catch (Exception e){
            if(redisTemplate.hasKey(key)){
                //恢复缓存该日程已经挂号的数量
                redisTemplate.opsForHash().increment(key,"num",-1);
            }
            throw e;
        }
        return null;
    }

    @Override
    public ArrayList<HashMap>searchDeptSubDoctorPlanInDay (Map param) {
        ArrayList<HashMap> list = doctorWorkPlanDao.searchDeptSubDoctorPlanInDay(param);
        return list;
    }
}
